package com.webcamtracker.tracker.example.portal;

import com.webcamtracker.move.tracking.TrackingEvent;
import com.webcamtracker.move.tracking.TrackingEventListener;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;

public class PortalPanel extends JPanel
        implements TrackingEventListener {

    /**
     * The size of the canvas in witch the point varies.
     * The top-left location will be (-pointCanvasSize.getWidth/2 , -pointCanvasSize.getHeight/2)
     * The center location will be (0 , 0)
     * The bottom-right location will be (+pointCanvasSize.getWidth/2 , +pointCanvasSize.getHeight/2)
     */
    private final int canvasWidth;
    private final int canvasHeight;

    /**
     * Point Location > It varies:
     * on the "x" axis between (-pointCanvasSize.getWidth/2  : +pointCanvasSize.getWidth/2)
     * on the "y" axis between (-pointCanvasSize.getHeight/2 : +pointCanvasSize.getHeight/2)
     */
    private Point pointLocation;

    private int imageWidth;
    private int imageHeight;

    private static final int PORTAL_WIDTH = 900;
    private static final int PORTAL_HEIGHT = 500;

    private BufferedImage image;

    private int paintFromY;
    private int paintFromX;

    private float zoom;
    private boolean zoomActivated = true;

    public PortalPanel(Dimension dimension) {
        setPreferredSize(new Dimension(PORTAL_WIDTH, PORTAL_HEIGHT));
        this.image = createBufferedImage();
        this.canvasWidth = (int) dimension.getWidth();
        this.canvasHeight = (int) dimension.getHeight();
        this.pointLocation = new Point(canvasWidth / 2, canvasHeight / 2);
        this.paintFromX = getNextPaintFromX();
        this.paintFromY = getNextPaintFromY();
        this.zoom = 1;
    }

    private BufferedImage createBufferedImage() {
        File file = new File("tracker/img/landscape3.jpg");
        Image img = new ImageIcon(file.getPath()).getImage();

        this.imageWidth = img.getWidth(null);
        this.imageHeight = img.getHeight(null);

        BufferedImage buffImg =
                new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = buffImg.createGraphics();
        g.drawImage(img, null, null);

        return buffImg;
    }

    public void paint(Graphics g) {
        Graphics2D graphics = (Graphics2D) g;

        graphics.clearRect(0, 0, PORTAL_WIDTH, PORTAL_HEIGHT);

        BufferedImage subimage = this.image.getSubimage((int) (this.paintFromX / zoom), (int) (this.paintFromY / zoom),
                (int) (PORTAL_WIDTH / zoom), (int) (PORTAL_HEIGHT / zoom));

        graphics.drawImage(subimage, 0, 0, PORTAL_WIDTH, PORTAL_HEIGHT, null);
    }

    private int getNextPaintFromY() {
        /**
         * The portalCenterY translates the pointLocation.y to the PORTAL center location on the IMAGE "y" axis
         */
        final int portalCenterY = this.pointLocation.y * (getScaled(imageHeight) - PORTAL_HEIGHT) / canvasHeight;

        /**
         * Returns the point in which the paint of the image starts (y axis)
         */
        final int unboundPaintFromY = portalCenterY - (PORTAL_HEIGHT / 2);

        return unboundPaintFromY < 0 ? 0 : Math.min(unboundPaintFromY, getScaled(imageHeight) - PORTAL_HEIGHT);
    }

    private int getNextPaintFromX() {
        /**
         * The portalCenterX translates the pointLocation.x to the PORTAL center location on the IMAGE "x" axis
         */
        final int portalCenterX = this.pointLocation.x * (getScaled(imageWidth) - PORTAL_WIDTH) / canvasWidth;

        /**
         * Returns the point in which the paint of the image starts (x axis)
         */
        final int unboundPaintFromX = portalCenterX - (PORTAL_WIDTH / 2);

        return (int) (unboundPaintFromX < 0 ? 0 : Math.min(unboundPaintFromX, getScaled(imageWidth) - PORTAL_WIDTH));
    }

    private int getScaled(int size) {
        return (int) (zoom * size);
    }

    public void trackingEventDispatched(TrackingEvent event) {
        //Set the new center point
        setPointLocation(event.getPoint());

        //Reapint only if portal image changed
        if (portalChanged(event)) {
            if (zoomActivated) {
                this.zoom = event.getZoomFactor();
            }
            this.paintFromX = getNextPaintFromX();
            this.paintFromY = getNextPaintFromY();
            repaint();
        }
    }


    private boolean portalChanged(TrackingEvent dimension) {
        final float varZoom = this.zoom - dimension.getZoomFactor();
        final int varX = this.paintFromX - getNextPaintFromX();
        final int varY = this.paintFromY - getNextPaintFromY();

        if (!zoomActivated) {
            return Math.abs(varX) > PORTAL_WIDTH / 100 || Math.abs(varY) > PORTAL_HEIGHT / 100;
        } else {
            return Math.abs(varX) > PORTAL_WIDTH / 100 || Math.abs(varY) > PORTAL_HEIGHT / 100 || Math.abs(varZoom) > 0.06F;
        }
    }

    private void setPointLocation(Point point)
    {
        this.pointLocation.x = canvasWidth/2 + point.x;
        this.pointLocation.y = canvasHeight/2 + (canvasHeight - point.y);
    }
}
